package org.checkboo.math;


public class Vector3f {
	
	private float x;
	private float y;
	private float z;
	
	public Vector3f(float x, float y, float z) {
		super();
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	public Vector3f(Vector3f vector) {
		super();
		this.x = vector.x;
		this.y = vector.y;
		this.z = vector.z;
	}
	
	public Vector3f(){
		super();
		this.x = 0;
		this.y = 0;
		this.z = 0;
	}
	
	/**
	 * Adds supplied vector to <code>this</code> vector and stores it in this object.
	 * 
	 * @param vector vector to add
	 */
	public Vector3f add(Vector3f vector){
		this.x += vector.x;
		this.y += vector.y;
		this.z += vector.z;
		return this;
	}
	
	/**
	 * Adds supplied vector to <code>this</code> vector and stores it in result.
	 * 
	 * @param second supplied vector to add
	 * 
	 * @param result object to store the result in
	 */
	public void addRes(Vector3f second,Vector3f result){
		result.x = this.x + second.x;
		result.y = this.y + second.y;
		result.z = this.z + second.z;
	}
	
	/**
	 * Multiplies vectors and stores it in <code>this</code> object.
	 * 
	 * @param second vector to multiply by
	 */
	public Vector3f multiply(Vector3f second){
		this.x *= second.x;
		this.y *= second.y;
		this.z *= second.z;
		return this;
	}
	
	/**
	 * Multiplies vectors and stores it in result.
	 * 
	 * @param second vector to multiply by
	 * 
	 * @param result object to store the result in
	 */
	public void multiplyRes(Vector3f second, Vector3f result){
		result.x = this.x * second.x;
		result.y = this.y * second.y;
		result.z = this.z * second.z;
	}
	
	/**
	 * Multiplies <code>this</code> vector by scalar and stores it in this object.
	 * 
	 * @param scalar scalar to multiply by
	 */
	public Vector3f multiply(float scalar){
		this.x *= scalar;
		this.y *= scalar;
		this.z *= scalar;
		return this;
	}
	
	/**
	 * Multiplies <code>this</code> vector by scalar and stores it in result.
	 * 
	 * @param scalar scalar to multiply by
	 * 
	 * @param result object to store the result in
	 */
	public void multiplyRes(float scalar, Vector3f result){
		result.x = this.x * scalar;
		result.y = this.y * scalar;
		result.z = this.z * scalar;
	}
	
	/**
	 * Divides <code>this</code> vector with supplied vector
	 * @param second vector to divide with
	 * @return <code>this</code> vector
	 */
	public Vector3f divide(Vector3f second){
		this.x /= second.x;
		this.y /= second.y;
		this.z /= second.z;
		return this;
	}
	
	/**
	 * Divides <code>this</code> vector with supplied scalar
	 * @param scalar to divide with
	 * @return <code>this</code> vector
	 */
	public Vector3f divide(float scalar){
		this.x /= scalar;
		this.y /= scalar;
		this.z /= scalar;
		return this;
	}
	
	/**
	 * Divides <code>this</code> vector with supplied vector and stores it in result
	 * @param second vector to divide with
	 * @param result vector to store in
	 */
	public void divideRes(Vector3f second, Vector3f result){
		result.x = this.x / second.x;
		result.y = this.y / second.y;
		result.z = this.z / second.z;
	}
	
	/**
	 * Divides <code>this</code> vector with supplied scalar and stores it in result
	 * @param scalar to divide with
	 * @param result vector to store in
	 * @return <code>this</code> vector
	 */
	public void divideRes(float scalar, Vector3f result){
		result.x = this.x / scalar;
		result.y = this.y / scalar;
		result.z = this.z / scalar;
	}
	
	/**
	 * Subtracts supplied vector from <code>this</code> vector
	 * @param second vector to subtract
	 * @return
	 */
	public Vector3f subtract(Vector3f second){
		this.x -= second.x;
		this.y -= second.y;
		this.z -= second.z;
		return this;
	}
	
	/**
	 * Subtracts supplied vector from <code>this</code> vector and stores it in result
	 * @param second vector to subtract
	 * @param result vector to store in
	 */
	public void subtractRes(Vector3f second, Vector3f result){
		result.x = this.x - second.x;
		result.y = this.y - second.y;
		result.z = this.z - second.z;
	}
	
	/**
	 * Computes dot product of <code>this</code>vector and supplied vector
	 * @param second vector
	 * @return dot product
	 */
	public float dot(Vector3f second){
		return this.x*second.x+this.y*second.y+this.z*second.z;
	}
	
	/**
	 * Computes dot product of supplied vectors
	 * @param first vector
	 * @param second vector
	 * @return dot product
	 */
	public static float dot(Vector3f first, Vector3f second){
		return first.x*second.x+first.y*second.y+first.z*second.z;
	}
	
	/**
	 * Computes cross product of <code>this</code>vector and supplied vector
	 * @param second vector
	 * @return cross product
	 */
	public Vector3f cross(Vector3f second){
		this.x = this.y*second.z - this.z*second.y;
		this.y = this.z*second.x - this.x*second.z;
		this.x = this.x*second.y - this.y*second.x;
		return this;
	}
	
	/**
	 * Computes cross product of <code>this</code>vector and supplied vector and stores it in result
	 * @param second vector
	 * @param result vector to store in
	 */
	public void crossRes(Vector3f second, Vector3f result){
		result.x = this.y*second.z - this.z*second.y;
		result.y = this.z*second.x - this.x*second.z;
		result.z = this.x*second.y - this.y*second.x;
	}
	
	public static Vector3f cross(Vector3f first, Vector3f second){
		return first.cross(second);
	}
	
	public Vector3f transform(Matrix44f transform){
		Vector3f tmp = new Vector3f(this);
		this.x = transform.getV11()*tmp.getX() + transform.getV12()*tmp.getY()+transform.getV13()*tmp.getZ()+transform.getV14();
		this.y = transform.getV21()*tmp.getX() + transform.getV22()*tmp.getY()+transform.getV23()*tmp.getZ()+transform.getV24();
		this.z = transform.getV31()*tmp.getX() + transform.getV32()*tmp.getY()+transform.getV33()*tmp.getZ()+transform.getV34();
		
		return this;
	}
	
	public Vector3f transformRes(Matrix44f transform){
		Vector3f tmp = new Vector3f(this);
		tmp.x = transform.getV11()*this.getX() + transform.getV12()*this.getY()+transform.getV13()*this.getZ()+transform.getV14();
		tmp.y = transform.getV21()*this.getX() + transform.getV22()*this.getY()+transform.getV23()*this.getZ()+transform.getV24();
		tmp.z = transform.getV31()*this.getX() + transform.getV32()*this.getY()+transform.getV33()*this.getZ()+transform.getV34();
		
		return tmp;
	}

	/**
	 * Computes magnitude of <code>this</code> vector
	 * @return magnitude of <code>this</code> vector
	 */
	public float magnitude(){
		return FloatMath.sqrt(FloatMath.sqr(this.x)+FloatMath.sqr(this.y)+FloatMath.sqr(this.z));
	}
	
	/**
	 * Normalizes <code>this</code> vector
	 * @return <code>this</code> normalized vector
	 */
	public Vector3f normalize(){
		float magnitude = this.magnitude();
		if(magnitude != 0){
			return divide(magnitude);
		}
		return this;
	}
	
	/**
	 * Negates <code>this</code> vector
	 * @return <code>this</code> negative vector
	 */
	public Vector3f negate(){
		this.x = -this.x;
		this.y = -this.y;
		this.z = -this.z;
		return this;
	}

	/**
	 * Negates <code>this</code> vector and stores it in result
	 * @param result vector to store in
	 */
	public void negateRes(Vector3f result){
		result.x = -this.x;
		result.y = -this.y;
		result.z = -this.z;
	}
	
	public Vector3f zero(){
		this.x = this.y = this.z = 0;
		
		return this;
	}
	
	public void set(Vector3f vector){
		this.x = vector.x;
		this.y = vector.y;
		this.z = vector.z;
	}
	
	public void set(float x, float y, float z){
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	public float getX() {
		return x;
	}

	public void setX(float x) {
		this.x = x;
	}

	public float getY() {
		return y;
	}

	public void setY(float y) {
		this.y = y;
	}

	public float getZ() {
		return z;
	}

	public void setZ(float z) {
		this.z = z;
	}
	
	public String toString(){
		return "("+this.x+" , "+this.y+" , "+this.z+")";
	}
	
	public boolean equals(Vector3f second){
		if(null == second)
			return false;
		if(this.x == second.x && this.y == second.y && this.z == second.z)
			return true;
		else
			return false;
	}
	
}
